package com.neoCalf.neoUser.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.neoCalf.neoCommon.constants.Constants;
import com.neoCalf.neoCommon.entity.*;
import com.neoCalf.neoCommon.enums.*;
import com.neoCalf.neoCommon.exception.BusinessException;
import com.neoCalf.neoCommon.model.R;
import com.neoCalf.neoCommon.model.dto.SendMessageDto;
import com.neoCalf.neoCommon.model.dto.TokenUserInfoDto;
import com.neoCalf.neoCommon.redis.RedisComponent;
import com.neoCalf.neoCommon.redis.RedisTool;
import com.neoCalf.neoCommon.utils.*;
import com.neoCalf.neoUser.feigns.AuthServiceFeign;
import com.neoCalf.neoUser.feigns.ContactServiceFeign;
import com.neoCalf.neoUser.mapper.SysSettingMapper;
import com.neoCalf.neoUser.mapper.UserInfoBeautyMapper;
import com.neoCalf.neoUser.mapper.UserInfoMapper;
import com.neoCalf.neoUser.service.LoginService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

import static com.neoCalf.neoCommon.constants.Constants.REDIS_PREFIX_SYS_SETTING;
import static com.neoCalf.neoCommon.constants.Constants.SYS_SETTING_CHOOSE;


/**
 * @author 小牛
 * @version 1.0
 * @description TODO
 * @date 2025/4/8 11:33
 */
@Service(value = "loginService")
@Slf4j
public class LoginServiceImpl implements LoginService {
    @Resource
    private CodeUtils codeUtils;
    @Resource
    private UserInfoMapper userInfoMapper;
    @Resource
    private RedisTool<Object> redisTool;
    @Resource
    private RedisTool<Long> redisTool2;
    @Resource
    private UserInfoBeautyMapper userInfoBeautyMapper;
    @Resource
    private SysSettingMapper sysSettingMapper;
    @Autowired
    private ContactServiceFeign contactServiceFeign;
    @Autowired
    private RedisComponent<String> redisComponent;
    @Autowired
    private JwtUtil<Long> jwtUtil;
    @Autowired
    private SendUtils sendUtils;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void register(UserInfo userInfo, String checkCode) {
        String email = userInfo.getEmail();
        if (
                email == null ||
                        !DataTool.zeCheckStr(ZeEnum.ZE_USERNAME, userInfo.getNickName()) ||
                        !DataTool.zeCheckStr(ZeEnum.ZE_PASSWORD, userInfo.getPassword()) ||
                        !DataTool.zeCheckStr(ZeEnum.ZE_EMAIL, email)
        ) {
            throw new BusinessException(ResultCode.CODE_10017);
        }
        // 校验验证码
//        codeUtils.verifyCode(Constants.EMAIL_CODE_KEY + email, checkCode);

        // 校验邮箱是否已注册
        LambdaQueryWrapper<UserInfo> userInfoLambdaQueryWrapper = new LambdaQueryWrapper<>();
        userInfoLambdaQueryWrapper.eq(UserInfo::getEmail, email);
        UserInfo user = userInfoMapper.selectOne(userInfoLambdaQueryWrapper);
        if (user != null) {
            throw new BusinessException(ResultCode.CODE_10017, "邮箱已注册");
        }

        // 初始化用户信息
        LambdaQueryWrapper<UserInfoBeauty> userInfoBeautyLambdaQueryWrapper = new LambdaQueryWrapper<>();
        userInfoBeautyLambdaQueryWrapper.eq(UserInfoBeauty::getEmail, email);

        UserInfoBeauty userInfoBeauty = userInfoBeautyMapper.selectOne(userInfoBeautyLambdaQueryWrapper);
        boolean beautyAccountStatus = userInfoBeauty != null && userInfoBeauty.getStatus().equals(BeautyAccountStatusEnum.NO_USE.getStatus());
        if (beautyAccountStatus) {
            //设置靓号id
            userInfo.setUserId(userInfoBeauty.getUserId());
            userInfo.setRole(RoleEnum.ADMIN.getRole());
        } else {
            userInfo.setUserId(DataTool.getUserId());
            userInfo.setRole(RoleEnum.USER.getRole());
        }
        long date = new Date().getTime();
        userInfo.setJoinType(JoinTypeEnum.APPLY.getJoinType());
        userInfo.setStatus(UserStatusEnum.NORMAL.getType());
        userInfo.setCreateTime(date);
        userInfo.setPassword(Md5Util.encodeByMd5(userInfo.getPassword()));
        userInfo.setLastOffTime(date);
        int result = userInfoMapper.insert(userInfo);
        DataTool.checkAffectRow(result, "服务器出差了...");
        if (beautyAccountStatus) {
            LambdaUpdateWrapper<UserInfoBeauty> userInfoBeautyLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
            userInfoBeautyLambdaUpdateWrapper.eq(UserInfoBeauty::getUserId, userInfoBeauty.getUserId());
            userInfoBeautyLambdaUpdateWrapper.set(UserInfoBeauty::getStatus, BeautyAccountStatusEnum.USE_ED.getStatus());
            userInfoBeautyMapper.update(null, userInfoBeautyLambdaUpdateWrapper);
        }
        // 创建机器人
        SysSetting sysSetting = getSysSetting();
        String userId = userInfo.getUserId();
        String robotId = sysSetting.getRobotUid();
        String robotNickName = sysSetting.getRobotNickName();
        Long sendTime = System.currentTimeMillis();
        UserContact userContact = new UserContact(userId, robotId, UserContactTypeEnum.ROBOT.getType(), sendTime, UserContactStatusEnum.FRIEND.getStatus(), sendTime);
        userContact.setContactName(robotNickName);
        userContact.setWelcomeMessage(sysSetting.getRobotWelcome());
        R<String> stringR = contactServiceFeign.addContact(userContact);
        if (!stringR.getCode().equals(ResultCode.Success.getCode())) {
            throw new BusinessException(ResultCode.BusinessError, "创建机器人失败");
        }
    }

    @Override
    public TokenUserInfoDto login(String email, String password) {
        LambdaQueryWrapper<UserInfo> userInfoLambdaQueryWrapper = new LambdaQueryWrapper<>();
        userInfoLambdaQueryWrapper.eq(UserInfo::getEmail, email);
        userInfoLambdaQueryWrapper.eq(UserInfo::getPassword, Md5Util.encodeByMd5(password));
        UserInfo userInfo = userInfoMapper.selectOne(userInfoLambdaQueryWrapper);
        if (userInfo == null) {
            throw new BusinessException(ResultCode.CODE_10017, "账号或密码错误");
        }
        return commonLogin(userInfo);
    }

    @Override
    public TokenUserInfoDto phoneLogin(String phone, String checkCode) {
        // 校验验证码
//        codeUtils.verifyCode(Constants.PHONE_CODE_KEY + phone, checkCode);
        // 校验是否绑定了手机号
        LambdaQueryWrapper<UserInfo> userInfoLambdaQueryWrapper = new LambdaQueryWrapper<>();
        userInfoLambdaQueryWrapper.eq(UserInfo::getPhone, phone);
        UserInfo userInfo = userInfoMapper.selectOne(userInfoLambdaQueryWrapper);
        if (userInfo == null) {
            throw new BusinessException(ResultCode.CODE_10017, "手机号未绑定");
        }
        return commonLogin(userInfo);
    }

    @Override
    public SysSetting getSysSetting() {
        Object object = redisTool.get(REDIS_PREFIX_SYS_SETTING);
        if (object != null) {
            return (SysSetting) object;
        } else {
            log.info("缓存中未找到系统设置,将从数据库查询");
            SysSetting sysSetting = sysSettingMapper.selectById(SYS_SETTING_CHOOSE);
            redisTool.set(REDIS_PREFIX_SYS_SETTING, sysSetting);
            return sysSetting;
        }
    }

    @Override
    public void forget(String email, String password, String checkCode) {
        // 校验验证码
        codeUtils.verifyCode(Constants.EMAIL_CODE_KEY + email, checkCode);
        // 校验邮箱
        LambdaQueryWrapper<UserInfo> userInfoLambdaQueryWrapper = new LambdaQueryWrapper<>();
        userInfoLambdaQueryWrapper.eq(UserInfo::getEmail, email);
        UserInfo userInfo = userInfoMapper.selectOne(userInfoLambdaQueryWrapper);
        if (userInfo == null) {
            throw new BusinessException(ResultCode.CODE_10017, "邮箱不存在");
        }
        userInfo.setPassword(Md5Util.encodeByMd5(password));
        int result = userInfoMapper.updateById(userInfo);
        DataTool.checkAffectRow(result, "服务器出差了...");
    }

    public TokenUserInfoDto commonLogin(UserInfo userInfo) {
        // 账号状态校验
        redisComponent.checkAccountStatus(userInfo.getUserId());
        // 修改最后一次登录时间
        LambdaUpdateWrapper<UserInfo> userInfoLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        userInfoLambdaUpdateWrapper.eq(UserInfo::getUserId, userInfo.getUserId());
        userInfoLambdaUpdateWrapper.set(UserInfo::getLastLoginTime, new Date().getTime());
        userInfoMapper.update(null, userInfoLambdaUpdateWrapper);

        // 查询联系人
        // 查询群组
        redisComponent.delContactList(userInfo.getUserId());
        R<List<UserContact>> listR = contactServiceFeign.selectContactList(userInfo.getUserId());
        DataTool.checkAffectRow(listR.getCode(), "查询联系人失败");
        List<UserContact> userContactList = listR.getResult();
        List<String> userContactIdList = userContactList
                .stream()
                .filter(e -> getContactTypeEnum(e) == UserContactTypeEnum.USER || getContactTypeEnum(e) == UserContactTypeEnum.ROBOT || getContactTypeEnum(e) == UserContactTypeEnum.SYSTEM)
                .map(UserContact::getContactId)
                .collect(Collectors.toList());
        List<String> groupIdList = userContactList
                .stream()
                .filter(e -> getContactTypeEnum(e) == UserContactTypeEnum.GROUP)
                .map(UserContact::getContactId)
                .collect(Collectors.toList());
        redisComponent.saveContactList(userInfo.getUserId(), userContactIdList, groupIdList);

        TokenUserInfoDto tokenUserInfoDto = getTokenUserInfoDto(userInfo);
        redisTool2.set(tokenUserInfoDto.getToken(), userInfo.getLastOffTime());
        return tokenUserInfoDto;
    }

    public UserContactTypeEnum getContactTypeEnum(UserContact userContact) throws BusinessException {
        return DataTool.getContactTypeEnum(userContact.getContactId());
    }

    public TokenUserInfoDto getTokenUserInfoDto(UserInfo userInfo) {
        String token = jwtUtil.generateJwtToken(userInfo);
        List<String> userHeartBeatList = redisComponent.getUserHeartBeatList();
        if (userHeartBeatList.contains(userInfo.getUserId())) {
            sendUtils.FORCE_OFF_LINE(userInfo.getUserId());
            return new TokenUserInfoDto(userInfo, token, true);
        }
        return new TokenUserInfoDto(userInfo, token, false);
    }
}
